/** 
// https://github.com/alsey/wxbarcode
// 最后一位显示 _ 问题
// https://github.com/alsey/wxbarcode/issues/2
// //ok some type of shift is nessecary if (shifter != -1) { result.push(shifter); result.push(codeValue(chr1));//把这里的chr2改成chr1即可。 }
**/

!(function () {
  var CHAR_TILDE = 126;
  var CODE_FNC1 = 102;

  var SET_STARTA = 103;
  var SET_STARTB = 104;
  var SET_STARTC = 105;
  var SET_SHIFT = 98;
  var SET_CODEA = 101;
  var SET_CODEB = 100;
  var SET_STOP = 106;


  var REPLACE_CODES = {
    CHAR_TILDE: CODE_FNC1 //~ corresponds to FNC1 in GS1-128 standard
  }

  var CODESET = {
    ANY: 1,
    AB: 2,
    A: 3,
    B: 4,
    C: 5
  };

  function getBytes(str) {
    var bytes = [];
    for (var i = 0; i < str.length; i++) {
      bytes.push(str.charCodeAt(i));
    }
    return bytes;
  }

  exports.code128 = function (ctx, text, width, height) {

    width = parseInt(width);

    height = parseInt(height);

    var codes = stringToCode128(text);

    var g = new Graphics(ctx, width, height);

    var barWeight = g.area.width / ((codes.length - 3) * 11 + 35);

    var x = g.area.left;
    var y = g.area.top;
    for (var i = 0; i < codes.length; i++) {
      var c = codes[i];
      //two bars at a time: 1 black and 1 white
      for (var bar = 0; bar < 8; bar += 2) {
        var barW = PATTERNS[c][bar] * barWeight;
        // var barH = height - y - this.border;
        var barH = height - y;
        var spcW = PATTERNS[c][bar + 1] * barWeight;

        //no need to draw if 0 width
        if (barW > 0) {
          g.fillFgRect(x, y, barW, barH);
        }

        x += barW + spcW;
      }
    }

    ctx.draw();
  }


  function stringToCode128(text) {

    var barc = {
      currcs: CODESET.C
    };

    var bytes = getBytes(text);
    //decide starting codeset
    var index = bytes[0] == CHAR_TILDE ? 1 : 0;

    var csa1 = bytes.length > 0 ? codeSetAllowedFor(bytes[index++]) : CODESET.AB;
    var csa2 = bytes.length > 0 ? codeSetAllowedFor(bytes[index++]) : CODESET.AB;
    barc.currcs = getBestStartSet(csa1, csa2);
    barc.currcs = perhapsCodeC(bytes, barc.currcs);

    //if no codeset changes this will end up with bytes.length+3
    //start, checksum and stop
    var codes = new Array();

    switch (barc.currcs) {
      case CODESET.A:
        codes.push(SET_STARTA);
        break;
      case CODESET.B:
        codes.push(SET_STARTB);
        break;
      default:
        codes.push(SET_STARTC);
        break;
    }


    for (var i = 0; i < bytes.length; i++) {
      var b1 = bytes[i]; //get the first of a pair
      //should we translate/replace
      if (b1 in REPLACE_CODES) {
        codes.push(REPLACE_CODES[b1]);
        i++ //jump to next
        b1 = bytes[i];
      }

      //get the next in the pair if possible
      var b2 = bytes.length > (i + 1) ? bytes[i + 1] : -1;

      codes = codes.concat(codesForChar(b1, b2, barc.currcs));
      //code C takes 2 chars each time
      if (barc.currcs == CODESET.C) i++;
    }

    //calculate checksum according to Code 128 standards
    var checksum = codes[0];
    for (var weight = 1; weight < codes.length; weight++) {
      checksum += (weight * codes[weight]);
    }
    codes.push(checksum % 103);

    codes.push(SET_STOP);

    //encoding should now be complete
    return codes;

    function getBestStartSet(csa1, csa2) {
      //tries to figure out the best codeset
      //to start with to get the most compact code
      var vote = 0;
      vote += csa1 == CODESET.A ? 1 : 0;
      vote += csa1 == CODESET.B ? -1 : 0;
      vote += csa2 == CODESET.A ? 1 : 0;
      vote += csa2 == CODESET.B ? -1 : 0;
      //tie goes to B due to my own predudices
      return vote > 0 ? CODESET.A : CODESET.B;
    }

    function perhapsCodeC(bytes, codeset) {
      for (var i = 0; i < bytes.length; i++) {
        var b = bytes[i]
        if ((b < 48 || b > 57) && b != CHAR_TILDE)
          return codeset;
      }
      return CODESET.C;
    }

    //chr1 is current byte
    //chr2 is the next byte to process. looks ahead.
    function codesForChar(chr1, chr2, currcs) {
      var result = [];
      var shifter = -1;

      if (charCompatible(chr1, currcs)) {
        if (currcs == CODESET.C) {
          if (chr2 == -1) {
            shifter = SET_CODEB;
            currcs = CODESET.B;
          }
          else if ((chr2 != -1) && !charCompatible(chr2, currcs)) {
            //need to check ahead as well
            if (charCompatible(chr2, CODESET.A)) {
              shifter = SET_CODEA;
              currcs = CODESET.A;
            }
            else {
              shifter = SET_CODEB;
              currcs = CODESET.B;
            }
          }
        }
      }
      else {
        //if there is a next char AND that next char is also not compatible
        if ((chr2 != -1) && !charCompatible(chr2, currcs)) {
          //need to switch code sets
          switch (currcs) {
            case CODESET.A:
              shifter = SET_CODEB;
              currcs = CODESET.B;
              break;
            case CODESET.B:
              shifter = SET_CODEA;
              currcs = CODESET.A;
              break;
          }
        }
        else {
          //no need to shift code sets, a temporary SHIFT will suffice
          shifter = SET_SHIFT;
        }
      }

      //ok some type of shift is nessecary
      if (shifter != -1) {
        result.push(shifter);
        result.push(codeValue(chr1));
      }
      else {
        if (currcs == CODESET.C) {
          //include next as well
          result.push(codeValue(chr1, chr2));
        }
        else {
          result.push(codeValue(chr1));
        }
      }
      barc.currcs = currcs;

      return result;
    }
  }

  //reduce the ascii code to fit into the Code128 char table
  function codeValue(chr1, chr2) {
    if (typeof chr2 == "undefined") {
      return chr1 >= 32 ? chr1 - 32 : chr1 + 64;
    }
    else {
      return parseInt(String.fromCharCode(chr1) + String.fromCharCode(chr2));
    }
  }

  function charCompatible(chr, codeset) {
    var csa = codeSetAllowedFor(chr);
    if (csa == CODESET.ANY) return true;
    //if we need to change from current
    if (csa == CODESET.AB) return true;
    if (csa == CODESET.A && codeset == CODESET.A) return true;
    if (csa == CODESET.B && codeset == CODESET.B) return true;
    return false;
  }

  function codeSetAllowedFor(chr) {
    if (chr >= 48 && chr <= 57) {
      //0-9
      return CODESET.ANY;
    }
    else if (chr >= 32 && chr <= 95) {
      //0-9 A-Z
      return CODESET.AB;
    }
    else {
      //if non printable
      return chr < 32 ? CODESET.A : CODESET.B;
    }
  }

  var Graphics = function (ctx, width, height) {

    this.width = width;
    this.height = height;
    this.quiet = Math.round(this.width / 40);

    this.border_size = 0;
    this.padding_width = 0;

    this.area = {
      width: width - this.padding_width * 2 - this.quiet * 2,
      height: height - this.border_size * 2,
      top: this.border_size - 4,
      left: this.padding_width + this.quiet
    };

    this.ctx = ctx;
    this.fg = "#000000";
    this.bg = "#ffffff";

    // fill background
    this.fillBgRect(0, 0, width, height);

    // fill center to create border
    this.fillBgRect(0, this.border_size, width, height - this.border_size * 2);
  }

  //use native color
  Graphics.prototype._fillRect = function (x, y, width, height, color) {
    this.ctx.setFillStyle(color)
    this.ctx.fillRect(x, y, width, height)
  }

  Graphics.prototype.fillFgRect = function (x, y, width, height) {
    this._fillRect(x, y, width, height, this.fg);
  }

  Graphics.prototype.fillBgRect = function (x, y, width, height) {
    this._fillRect(x, y, width, height, this.bg);
  }

  var PATTERNS = [
    [2, 1, 2, 2, 2, 2, 0, 0],  // 0
    [2, 2, 2, 1, 2, 2, 0, 0],  // 1
    [2, 2, 2, 2, 2, 1, 0, 0],  // 2
    [1, 2, 1, 2, 2, 3, 0, 0],  // 3
    [1, 2, 1, 3, 2, 2, 0, 0],  // 4
    [1, 3, 1, 2, 2, 2, 0, 0],  // 5
    [1, 2, 2, 2, 1, 3, 0, 0],  // 6
    [1, 2, 2, 3, 1, 2, 0, 0],  // 7
    [1, 3, 2, 2, 1, 2, 0, 0],  // 8
    [2, 2, 1, 2, 1, 3, 0, 0],  // 9
    [2, 2, 1, 3, 1, 2, 0, 0],  // 10
    [2, 3, 1, 2, 1, 2, 0, 0],  // 11
    [1, 1, 2, 2, 3, 2, 0, 0],  // 12
    [1, 2, 2, 1, 3, 2, 0, 0],  // 13
    [1, 2, 2, 2, 3, 1, 0, 0],  // 14
    [1, 1, 3, 2, 2, 2, 0, 0],  // 15
    [1, 2, 3, 1, 2, 2, 0, 0],  // 16
    [1, 2, 3, 2, 2, 1, 0, 0],  // 17
    [2, 2, 3, 2, 1, 1, 0, 0],  // 18
    [2, 2, 1, 1, 3, 2, 0, 0],  // 19
    [2, 2, 1, 2, 3, 1, 0, 0],  // 20
    [2, 1, 3, 2, 1, 2, 0, 0],  // 21
    [2, 2, 3, 1, 1, 2, 0, 0],  // 22
    [3, 1, 2, 1, 3, 1, 0, 0],  // 23
    [3, 1, 1, 2, 2, 2, 0, 0],  // 24
    [3, 2, 1, 1, 2, 2, 0, 0],  // 25
    [3, 2, 1, 2, 2, 1, 0, 0],  // 26
    [3, 1, 2, 2, 1, 2, 0, 0],  // 27
    [3, 2, 2, 1, 1, 2, 0, 0],  // 28
    [3, 2, 2, 2, 1, 1, 0, 0],  // 29
    [2, 1, 2, 1, 2, 3, 0, 0],  // 30
    [2, 1, 2, 3, 2, 1, 0, 0],  // 31
    [2, 3, 2, 1, 2, 1, 0, 0],  // 32
    [1, 1, 1, 3, 2, 3, 0, 0],  // 33
    [1, 3, 1, 1, 2, 3, 0, 0],  // 34
    [1, 3, 1, 3, 2, 1, 0, 0],  // 35
    [1, 1, 2, 3, 1, 3, 0, 0],  // 36
    [1, 3, 2, 1, 1, 3, 0, 0],  // 37
    [1, 3, 2, 3, 1, 1, 0, 0],  // 38
    [2, 1, 1, 3, 1, 3, 0, 0],  // 39
    [2, 3, 1, 1, 1, 3, 0, 0],  // 40
    [2, 3, 1, 3, 1, 1, 0, 0],  // 41
    [1, 1, 2, 1, 3, 3, 0, 0],  // 42
    [1, 1, 2, 3, 3, 1, 0, 0],  // 43
    [1, 3, 2, 1, 3, 1, 0, 0],  // 44
    [1, 1, 3, 1, 2, 3, 0, 0],  // 45
    [1, 1, 3, 3, 2, 1, 0, 0],  // 46
    [1, 3, 3, 1, 2, 1, 0, 0],  // 47
    [3, 1, 3, 1, 2, 1, 0, 0],  // 48
    [2, 1, 1, 3, 3, 1, 0, 0],  // 49
    [2, 3, 1, 1, 3, 1, 0, 0],  // 50
    [2, 1, 3, 1, 1, 3, 0, 0],  // 51
    [2, 1, 3, 3, 1, 1, 0, 0],  // 52
    [2, 1, 3, 1, 3, 1, 0, 0],  // 53
    [3, 1, 1, 1, 2, 3, 0, 0],  // 54
    [3, 1, 1, 3, 2, 1, 0, 0],  // 55
    [3, 3, 1, 1, 2, 1, 0, 0],  // 56
    [3, 1, 2, 1, 1, 3, 0, 0],  // 57
    [3, 1, 2, 3, 1, 1, 0, 0],  // 58
    [3, 3, 2, 1, 1, 1, 0, 0],  // 59
    [3, 1, 4, 1, 1, 1, 0, 0],  // 60
    [2, 2, 1, 4, 1, 1, 0, 0],  // 61
    [4, 3, 1, 1, 1, 1, 0, 0],  // 62
    [1, 1, 1, 2, 2, 4, 0, 0],  // 63
    [1, 1, 1, 4, 2, 2, 0, 0],  // 64
    [1, 2, 1, 1, 2, 4, 0, 0],  // 65
    [1, 2, 1, 4, 2, 1, 0, 0],  // 66
    [1, 4, 1, 1, 2, 2, 0, 0],  // 67
    [1, 4, 1, 2, 2, 1, 0, 0],  // 68
    [1, 1, 2, 2, 1, 4, 0, 0],  // 69
    [1, 1, 2, 4, 1, 2, 0, 0],  // 70
    [1, 2, 2, 1, 1, 4, 0, 0],  // 71
    [1, 2, 2, 4, 1, 1, 0, 0],  // 72
    [1, 4, 2, 1, 1, 2, 0, 0],  // 73
    [1, 4, 2, 2, 1, 1, 0, 0],  // 74
    [2, 4, 1, 2, 1, 1, 0, 0],  // 75
    [2, 2, 1, 1, 1, 4, 0, 0],  // 76
    [4, 1, 3, 1, 1, 1, 0, 0],  // 77
    [2, 4, 1, 1, 1, 2, 0, 0],  // 78
    [1, 3, 4, 1, 1, 1, 0, 0],  // 79
    [1, 1, 1, 2, 4, 2, 0, 0],  // 80
    [1, 2, 1, 1, 4, 2, 0, 0],  // 81
    [1, 2, 1, 2, 4, 1, 0, 0],  // 82
    [1, 1, 4, 2, 1, 2, 0, 0],  // 83
    [1, 2, 4, 1, 1, 2, 0, 0],  // 84
    [1, 2, 4, 2, 1, 1, 0, 0],  // 85
    [4, 1, 1, 2, 1, 2, 0, 0],  // 86
    [4, 2, 1, 1, 1, 2, 0, 0],  // 87
    [4, 2, 1, 2, 1, 1, 0, 0],  // 88
    [2, 1, 2, 1, 4, 1, 0, 0],  // 89
    [2, 1, 4, 1, 2, 1, 0, 0],  // 90
    [4, 1, 2, 1, 2, 1, 0, 0],  // 91
    [1, 1, 1, 1, 4, 3, 0, 0],  // 92
    [1, 1, 1, 3, 4, 1, 0, 0],  // 93
    [1, 3, 1, 1, 4, 1, 0, 0],  // 94
    [1, 1, 4, 1, 1, 3, 0, 0],  // 95
    [1, 1, 4, 3, 1, 1, 0, 0],  // 96
    [4, 1, 1, 1, 1, 3, 0, 0],  // 97
    [4, 1, 1, 3, 1, 1, 0, 0],  // 98
    [1, 1, 3, 1, 4, 1, 0, 0],  // 99
    [1, 1, 4, 1, 3, 1, 0, 0],  // 100
    [3, 1, 1, 1, 4, 1, 0, 0],  // 101
    [4, 1, 1, 1, 3, 1, 0, 0],  // 102
    [2, 1, 1, 4, 1, 2, 0, 0],  // 103
    [2, 1, 1, 2, 1, 4, 0, 0],  // 104
    [2, 1, 1, 2, 3, 2, 0, 0],  // 105
    [2, 3, 3, 1, 1, 1, 2, 0]   // 106
  ]
})();